﻿using PSL.ST;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PSL
{
    /// <summary>
    /// Core of the psl-engine, the runtime executes the script-code
    /// </summary>
    public class PSLEngine
    {
        #region Private Member
        private PSLRuntime runtime;
        #endregion

        #region Constructor
        /// <summary>
        /// Create PSL-Runtime
        /// </summary>
        /// <param name="Engine">PSL-Engine instance</param>
        public PSLEngine(PSLRuntime Engine)
        {
            runtime = Engine;
        }
        #endregion

        #region Public Methods
        /// <summary>
        /// Execute the given syntax tree
        /// </summary>
        /// <param name="SyntaxTree">Instance of an syntax-tree</param>
        internal void Execute(SyntaxTree SyntaxTree)
        {
            System.Diagnostics.Debugger.Launch();
            SyntaxTree.RootNode.Execute(this);
        }

        internal dynamic CallInternFunction(DeclareFunctionNode Node)
        {
            return null;
        }

        #region [CallFunction]
        /// <summary>
        /// call a function
        /// </summary>
        /// <param name="CallFunctionNode">Function instance</param>
        /// <returns></returns>
        internal dynamic CallFunction(CallFunctionNode CallFunctionNode)
        {
            PSLParameter parameter = new PSLParameter(this);
            parameter.FunctionName = (CallFunctionNode as CallFunctionNode).FunctionName;

            List<object> paramList = new List<object>();

            // Get all parameter
            foreach (ST.ParameterNode param in CallFunctionNode.FindChildrenOfType<ST.ParameterNode>())
            {
                if (true)//!parameter.ContainsKey(param.ParameterName))
                {
                    //parameter.Add(param.ParameterName, GetParameterValue(param));
                    paramList.Add(GetParameterValue(param));
                }
                else
                {
                    throw new Exception("The parameter " + param.ParameterName + " already used (Function: " + CallFunctionNode.FunctionName + ").");
                }
            }

            // Call function and return the value
            return runtime.FunctionStack.Call(CallFunctionNode.FunctionName, paramList.ToArray());
        }
        #endregion

        #region [Get ConstantValue]
        /// <summary>
        /// Get constant value
        /// </summary>
        /// <param name="Node">Constant-Value</param>
        /// <returns>return the value of the constant</returns>
        internal dynamic GetConstantValue(ST.ValueNode Node)
        {
            return Node.Value;
        }
        #endregion

        #region [Get ParameterValue]
        /// <summary>
        /// Get parameter-value by parameternode
        /// </summary>
        /// <param name="Node">Node instance</param>
        /// <returns>Value of the parameter</returns>
        internal dynamic GetParameterValue(ST.ParameterNode Node)
        {
            if (Node.Children.Count == 1)
            {
                TreeNode child = Node.Children[0];
                dynamic returnValue = null;

                if (child is ValueNode)
                {
                    returnValue = GetConstantValue(child as ValueNode);
                }
                else if (child is CallFunctionNode)
                {
                    returnValue = this.CallFunction(child as CallFunctionNode);
                }

                return returnValue;
            }
            else
            {
                throw new Exception("Error in the parameter: " + Node.ParameterName + ". No value set.");
            }
        }
        #endregion

        #endregion

        #region Private Methods
        public PSLRuntime Runtime
        {
            get { return runtime; }
            set { runtime = value; }
        }
        #endregion
    }
}
